import numpy as np
import pandas as pd
import time


class Avellaneda(object):

    def __init__(self) -> None:
        ...

    def set_params(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    def check_params(self):
        '''
        lp/sp:  [1, 10]
        s:      any
        sigma:  any
        gamma:  [0.1, 1]
        k:      1
        '''
        if not all([0.1<=self.gamma<=1, 0<self.sigma<1]):
            raise BaseException('Parameter verification fails')
        return True

    def preprocess_params(self):
        # s
        # self.s = np.log(1+self.s)

        # ls/sp normalized [1,10]
        total = self.lp + self.sp
        if total == 0:
            self.lp, self.sp = 5, 5
        else:
            self.lp, self.sp = 10-self.lp/total, 10-self.sp/total
        return True
        
    def postprocess_shift(self, shift):
        if self.max_shift < shift:
            shift = self.max_shift
        elif shift < -self.max_shift:
            shift = -self.max_shift
        else:
            ...
        return shift
    
    def postprocess_spread(self, spread):
        if self.max_spread < spread:
            spread = self.max_spread
        elif spread < -self.max_spread:
            spread = -self.max_spread
        else:
            ...
        return spread

    @property
    def t(self):
        # TODO: change 
        return int(time.time())%self.T
        
    @property
    def q(self):
        '''
        target inventory percentage
            fix q = s*q
        '''
        q = self.s*(self.lp-self.sp)
        return q

    @property
    def shift(self):
        print(('delta t', self.T-self.t))
        shift = self.q*self.gamma*self.sigma**2*(self.T-self.t)
        shift = self.postprocess_shift(shift)
        return shift

    def opt_price(self):
        '''
        using stock calc price
        math:
            r(s,q,t) = s - q*\gamma*\sigma^2(T-t)
        '''
        price = self.s - self.shift
        return price

    def opt_spread(self):
        '''
        math
            \delta^a+\delta^b = \gamma*\sigma^2(T-t)+2/\gamma*ln(1+\gamma/k)
        '''
        spread = self.gamma*self.sigma**2*(self.T-self.t)+2/self.gamma*np.log(1+self.gamma/self.k)
        spread = self.postprocess_spread(spread)
        return spread

    def level1(self, price, spread):
        lp, sp = price-(price*spread*0.5), price+(price*spread*0.5)
        return lp, sp

    def pricing(self, **kwargs):
        self.set_params(**kwargs)
        self.check_params()
        self.preprocess_params()

        price = self.opt_price()
        spread = self.opt_spread()
        l1 = self.level1(price, spread)
        return price, spread, l1


class Pricing(object):

    def __init__(self) -> None:
        pass

    def as_model(self, lp, sp, s, sigma):
        as_model = Avellaneda()
        init_params = {
            'max_shift': 0.0004,    # fees
            'max_spread': 0.001,    # spread

            'T': 60*60,             # second
            'tar_lp': 50,       # unuse(reversed), default 50-50
            'tar_sp': 50,       # unuse(reversed), default 50-50
        }
        as_model.set_params(**init_params)
        run_params = {
            'lp': lp,          # long position
            'sp': sp,          # short position

            's': s,            # mid price or index price

            'sigma': sigma,    # market volatility
            'gamma': 0.1,      # risk
            'k': 1e+6,         # liquid, fixed
        }
        price, spread, l1 = as_model.pricing(**run_params)
        print(f"pricing: \n\tinit price:{run_params.get('s')}\n\topt price:{price}\n\topt spread:{spread}\n\topt level1:{l1}")
        return price, spread, l1

